﻿#include "src/web_socket.h"
#include <string.h>
#include <algorithm>
#include <string>
#include <sstream>
#include <random>
#include "src/base64.h"
#include "src/sha_helper.h"
#include "src/byte_order.h"

/* Compare S1 and S2, ignoring case.  */
#ifdef _WIN32
#define cstr_compare_ignore_case(s1, s2) _stricmp(s1, s2)
#else
#define cstr_compare_ignore_case(s1, s2) strcasecmp(s1, s2)
#endif
namespace websocket {
static uint32_t GenerateUINT32() {
    static std::random_device rd;
    static std::default_random_engine engine(rd());
    static std::uniform_int_distribution<uint32_t> uid(0, UINT32_MAX);
    return uid(engine);
}

static void MaskingPayloadImpl(uint8_t mask_key[4], uint8_t *payload, uint32_t payload_size) {
    for (uint32_t i = 0; i < payload_size; i++) {
        payload[i] = payload[i] ^ mask_key[i % 4];
    }
}
static void MaskingPayloadImpl(uint32_t mask_key, std::string &frame, size_t offset) {
    uint8_t internal_mask_key[4];
    put_uint32_into_be_stream(internal_mask_key, mask_key);
    for (size_t i = offset; i < frame.size(); i++) {
        frame[i] = frame[i] ^ internal_mask_key[(i - offset) % 4];
    }
}

static std::string StringToLower(const std::string &key) {
    int count = static_cast<int>(key.size());
    if (count == 0) return std::string();
    std::string res;
    res.resize(count);
    for (int i = 0; i < count; i++) {
        res[i] = ::tolower(key[i]);
    }
    return res;
}

static std::string StringTrim(const std::string &str) {
    if (str.empty()) return str;
    const char *first = str.data();
    const char *end   = first + str.size();
    const char *last  = end - 1;
    while (first < end) {
        if (isspace(*first)) {
            first++;
        } else {
            break;
        }
    }

    if (first == end) {
        return std::string();
    }

    while (last > first) {
        if (isspace(*last)) {
            last--;
        } else {
            break;
        }
    }
    return str.substr(first - str.data(), last - first + 1);
}

static void SplitStringBySpace(const std::string &input, std::vector<std::string> &output) {
    output.clear();
    std::string sub;
    for (size_t i = 0; i < input.size(); i++) {
        if (isspace(input[i])) {
            if (!sub.empty()) {
                output.push_back(sub);
                sub.clear();
            }
        } else {
            sub.append(1, input[i]);
        }
    }
    if (!sub.empty()) {
        output.push_back(sub);
    }
}

static std::string BuildSecWebSocketAccept(const std::string &sec_websocket_kay) {
    std::string origin = sec_websocket_kay + std::string("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");

    helper::SHA1 sha1;
    sha1.Write(origin.data(), origin.size());

    unsigned char hash[20] = {0};
    sha1.Finalize(hash);

    std::string value;
    Base64Encode(std::string(reinterpret_cast<char *>(hash), sizeof(hash)), &value);
    return value;
}

int Handshaker::CheckPackageLength(const void *pdata, uint32_t data_size) {
    auto pstr = reinterpret_cast<const char *>(pdata);
    std::string headers(pstr, data_size);

    auto pos = headers.find("\r\n\r\n");
    if (pos == std::string::npos) {
        return 0;
    }
    return static_cast<int>(pos + 4);
}

Handshaker::Handshaker(const char *pdata, uint32_t data_size)
    : header_(pdata, data_size) {}

void Handshaker::ParsingHeaders() {

    std::istringstream s(header_);

    std::string line;
    std::getline(s, line);
    SplitStringBySpace(line, first_line_);
    std::swap(first_line_str_, line);

    while (std::getline(s, line)) {
        auto pos = line.find(":", 0);
        if (pos != std::string::npos) {
            std::string key   = StringTrim(line.substr(0, pos));
            std::string value = StringTrim(line.substr(pos + 1));
            // std::transform(key.begin(), key.end(), key.begin(), tolower);
            std::string nkey = StringToLower(key);
            auto result      = fields_.insert({nkey, value});
            if (!result.second) {
                result.first->second += "," + value;
            }
        }
    }
}
bool Handshaker::IsValidClientRequest() {

    /*
        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13
    */
    if (first_line_.size() != 3 || first_line_[0] != "GET" || first_line_[2] != "HTTP/1.1") {
        return false;
    }

    auto host       = FindField(WSH_HOST);
    auto upgrade    = FindField(WSH_UPGRADE);
    auto connection = FindField(WSH_CONNECTION);
    auto sec_ws_key = FindField(WSH_SEC_KEY);
    auto protocol   = FindField(WSH_SEC_PROTOCOL);
    auto version    = FindField(WSH_SEC_VERSION);

    if (cstr_compare_ignore_case(upgrade.data(), "websocket") != 0) {
        return false;
    }
    if (cstr_compare_ignore_case(connection.data(), "Upgrade") != 0) {
        return false;
    }
    if (host.empty() || sec_ws_key.empty() || version != WEBSOCKET_VERSION) {
        return false;
    }
    std::string decode_key;
    if (!Base64Decode(sec_ws_key, &decode_key)) {
        return false;
    }
    return true;
}

bool Handshaker::IsValidServerResponse(const std::string &sec_websocket_key,
                                       const std::string &sec_websocket_protocol) {
    /*
        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat
    */
    if (first_line_.size() < 3 || first_line_[1] != WEBSOCKET_SUCCESS_STATUS) {
        return false;
    }

    auto upgrade     = FindField(WSH_UPGRADE);
    auto connection  = FindField(WSH_CONNECTION);
    auto ws_accept   = FindField(WSH_SEC_ACCEPT);
    auto ws_protocol = FindField(WSH_SEC_PROTOCOL);

    if (ws_accept.empty()) {
        return false;
    }
    if (cstr_compare_ignore_case(upgrade.data(), "websocket") != 0) {
        return false;
    }
    if (cstr_compare_ignore_case(connection.data(), "Upgrade") != 0) {
        return false;
    }
    if (!sec_websocket_protocol.empty()) {
        if (ws_protocol.empty()) {
            return false;
        }
        auto pos = sec_websocket_protocol.find(ws_protocol);
        if (pos == std::string::npos) {
            return false;
        }
    }
    std::string result = BuildSecWebSocketAccept(sec_websocket_key);
    return result == ws_accept;
}

std::string Handshaker::FindField(const std::string &key) {
    std::string new_key = StringToLower(key);
    auto it             = fields_.find(new_key);
    if (it == fields_.end()) {
        return std::string();
    }
    return it->second;
}
std::string Handshaker::FirstLinePart1() {
    if (first_line_.empty()) {
        return std::string();
    }
    return first_line_[0];
}
std::string Handshaker::FirstLinePart2() {
    if (first_line_.size() < 2) {
        return std::string();
    }
    return first_line_[1];
}

std::string Handshaker::FirstLinePart3() {
    auto count = first_line_.size();
    if (count < 3) {
        return std::string();
    } else if (count == 3) {
        return first_line_[2];
    } else {
        std::string part;
        for (size_t i = 2; i < count; i++) {
            part += first_line_[i];
            if (i != count - 1) {
                part.append(1, ' ');
            }
        }
        return part;
    }
}
std::string Handshaker::FirstLineText() { return first_line_str_; }

int FrameContext::CheckFrameLength(const void *data, uint32_t size) {
    if (size < 2) {
        return 0;
    }

    auto input  = reinterpret_cast<const uint8_t *>(data);
    bool mask   = (input[1] & 0x80) != 0;
    uint8_t len = input[1] & 0x7F;

    int frame_len = mask ? 4 : 0;
    if (len == 126) {
        if (size < 4) {
            return 0;
        }
        frame_len += static_cast<int>(get_uint16_from_be_stream(&input[2])) + 4;
    } else if (len == 127) {
        if (size < 10) {
            return 0;
        }
        frame_len += static_cast<int>(get_uint64_from_be_stream(&input[2])) + 10;
    } else {
        frame_len += static_cast<int>(len) + 2;
    }

    return frame_len;
}

FrameContext::FrameContext(uint8_t *pdata, uint32_t data_size)
    : origin_ptr_(pdata)
    , origin_size_(data_size) {
    fin_         = false;
    opcode_      = WS_RESERVED7;
    mask_        = 0;
    payload_len_ = 0;
    payload_     = nullptr;
    data_offset_ = 0;
    memset(mask_key_, 0, sizeof(mask_key_));
}

FrameContext::~FrameContext() {}

bool FrameContext::ProcessData() {

    /*
          0                   1                   2                   3
          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
         +-+-+-+-+-------+-+-------------+-------------------------------+
         |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
         |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
         |N|V|V|V|       |S|             |   (if payload len==126/127)   |
         | |1|2|3|       |K|             |                               |
         +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
         |     Extended payload length continued, if payload len == 127  |
         + - - - - - - - - - - - - - - - +-------------------------------+
         |                               |Masking-key, if MASK set to 1  |
         +-------------------------------+-------------------------------+
         | Masking-key (continued)       |          Payload Data         |
         +-------------------------------- - - - - - - - - - - - - - - - +
         :                     Payload Data continued ...                :
         + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
         |                     Payload Data continued ...                |
         +---------------------------------------------------------------+
    */
    fin_    = (origin_ptr_[0] & 0x80) != 0;
    opcode_ = (origin_ptr_[0] & 0x0F);
    mask_   = (origin_ptr_[1] & 0x80) != 0;

    data_offset_ = 0;

    uint8_t len = origin_ptr_[1] & 0x7F;
    if (len == 0x7E) {
        uint16_t length = get_uint16_from_be_stream(&origin_ptr_[2]);
        payload_len_    = length;
        data_offset_    = 4;
    } else if (len == 0x7F) {
        uint64_t length = get_uint64_from_be_stream(&origin_ptr_[2]);
        payload_len_    = static_cast<uint32_t>(length);
        data_offset_    = 10;
    } else {
        payload_len_ = len;
        data_offset_ = 2;
    }

    if (mask_) {
        for (int i = 0; i < 4; i++) {
            mask_key_[i] = origin_ptr_[data_offset_++];
        }
        payload_ = origin_ptr_ + data_offset_;
        MaskingPayloadImpl(mask_key_, payload_, payload_len_);
    } else {
        payload_ = origin_ptr_ + data_offset_;
    }
    return true;
}

bool FrameContext::Fin() const { return fin_; }
int FrameContext::OpCode() const { return opcode_; }
bool FrameContext::Mask() const { return mask_; }
uint32_t FrameContext::PayloadSize() { return payload_len_; }
uint8_t *FrameContext::Payload() { return payload_; }
uint32_t FrameContext::MaskKey() { return get_uint32_from_be_stream(mask_key_); }
uint32_t FrameContext::FrameLength() { return data_offset_ + payload_len_; }

std::string BuildFramePrefix(bool fin, bool mask, int opcode, uint32_t mask_key, uint32_t size) {
    std::vector<uint8_t> output;
    output.reserve(16);

    if (fin) {
        output.push_back(static_cast<uint8_t>(opcode | 0x80));
    } else {
        output.push_back(static_cast<uint8_t>(opcode));
    }

    if (size < 0x7E) {
        output.push_back(static_cast<uint8_t>(size));
    } else if (size <= 0xffff) {
        uint8_t temp[2] = {0};
        put_uint16_into_be_stream(temp, static_cast<uint16_t>(size));
        output.push_back(0x7E);
        output.push_back(temp[0]);
        output.push_back(temp[1]);
    } else {
        uint8_t temp[8] = {0};
        put_uint64_into_be_stream(temp, static_cast<uint64_t>(size));
        output.push_back(0x7F);
        for (int i = 0; i < 8; i++) {
            output.push_back(temp[i]);
        }
    }

    if (mask) {
        output[1] |= 0x80;
        uint8_t temp[4] = {0};
        put_uint32_into_be_stream(temp, mask_key);
        for (size_t i = 0; i < sizeof(temp); i++) {
            output.push_back(temp[i]);
        }
    }

    return std::string(reinterpret_cast<const char *>(output.data()), output.size());
}

void MaskingPayload(const std::string &frame_prefix, uint8_t *payload, uint32_t payload_size) {
    auto ptr  = reinterpret_cast<const uint8_t *>(frame_prefix.data());
    bool mask = (ptr[1] & 0x80) != 0;
    if (mask) {
        ptr += frame_prefix.size() - 4;
        uint8_t mask_key[4] = {0};
        memcpy(mask_key, ptr, 4);
        MaskingPayloadImpl(mask_key, payload, payload_size);
    }
}

void MaskingPayload(uint32_t mask_key, uint8_t *payload, uint32_t payload_size) {
    uint8_t real_mask_key[4] = {0};
    put_uint32_into_be_stream(real_mask_key, mask_key);
    MaskingPayloadImpl(real_mask_key, payload, payload_size);
}

std::string BuildFrameWithPayload(const std::string &prefix, uint8_t *payload,
                                  uint32_t payload_size) {
    MaskingPayload(prefix, payload, payload_size);
    return CombinePrefixAndPayload(prefix, payload, payload_size);
}

std::string CombinePrefixAndPayload(const std::string &prefix, uint8_t *payload,
                                    uint32_t payload_size) {
    std::string frame;
    frame.reserve(prefix.size() + payload_size);
    frame += prefix;
    frame += std::string((char *)payload, payload_size);
    return frame;
}

std::string BuildSingleFrame(bool fin, bool mask, int opcode, uint32_t mask_key, uint8_t *payload,
                             uint32_t payload_size) {
    std::string prefix = BuildFramePrefix(fin, mask, opcode, mask_key, payload_size);
    return BuildFrameWithPayload(prefix, payload, payload_size);
}

int32_t CheckPackageLength(const void *data, uint32_t len) {
    if (len < 2) return 0;
    auto pdata = reinterpret_cast<const uint8_t *>(data);
    bool zrsv  = ((pdata[0] & 0x70) == 0);

    // Websocket: RSV1-RSV3 MUST BE ZERO
    // FIRSTBYTE: 0x80-0x8F OR 0x0-0xF
    if (zrsv && !isalpha(pdata[0])) {
        return FrameContext::CheckFrameLength(data, len);
    }
    return Handshaker::CheckPackageLength(data, len);
}

uint16_t GetCloseInfo(FrameContext *ctx, std::string *debug) {
    // close frame payload layout:
    // 2 bytes error code (bigendian) + debug binary
    if (ctx->OpCode() != WS_CLOSE || ctx->PayloadSize() < 2) {
        return 0;
    }

    uint16_t error_code = get_uint16_from_be_stream(ctx->Payload());
    error_code          = helper::IntegralChangeByteOrder(error_code);
    if (debug && ctx->PayloadSize() > 2) {
        debug->assign(reinterpret_cast<char *>(ctx->Payload() + 2), ctx->PayloadSize() - 2);
    }
    return error_code;
}

//////////////////////////////////////////////////////////////////////////////////////////////

static std::string BuildCloseFrameImpl(bool mask, uint32_t mask_key, Status status,
                                       const std::string &reason) {
    std::string prefix = BuildFramePrefix(true, mask, WS_CLOSE, mask_key, reason.size() + 2);
    std::string frame;
    frame.reserve(prefix.size() + 2 + reason.size());
    frame += prefix;
    uint8_t code[2] = {0};
    put_uint16_into_be_stream(code, static_cast<uint16_t>(status));
    frame.append(reinterpret_cast<char *>(code), 2);
    frame += reason;
    if (mask) {
        MaskingPayloadImpl(mask_key, frame, prefix.size());
    }
    return frame;
}

static std::string BuildPingPongFrameImpl(bool mask, int opcode, uint32_t mask_key,
                                          const std::string &data) {
    std::string prefix = BuildFramePrefix(true, mask, opcode, mask_key, data.size());
    std::string frame;
    frame.reserve(prefix.size() + data.size());
    frame += prefix;
    frame += data;
    if (mask) {
        MaskingPayloadImpl(mask_key, frame, prefix.size());
    }
    return frame;
}

static std::string BuildSingleDataFrameImpl(bool fin, bool mask, int opcode, uint32_t mask_key,
                                            const std::string &data) {
    std::string prefix = BuildFramePrefix(fin, mask, opcode, mask_key, data.size());
    std::string frame;
    frame.reserve(prefix.size() + data.size());
    frame += prefix;
    frame += data;
    if (mask) {
        MaskingPayloadImpl(mask_key, frame, prefix.size());
    }
    return frame;
}

static void BuildConsecutiveFramesImpl(bool mask, uint32_t mask_key,
                                       std::vector<std::string> *frames, const std::string &data,
                                       uint32_t max_data_size_of_single_frame,
                                       bool text_or_binary) {
    frames->clear();
    uint32_t data_size = data.size();
    if (max_data_size_of_single_frame == 0) {
        max_data_size_of_single_frame = 0xffff;
    }
    int opcode = text_or_binary ? WS_TEXT : WS_BINARY;
    if (data_size <= max_data_size_of_single_frame) {
        std::string frame = BuildSingleDataFrameImpl(true, mask, opcode, mask_key, data);
        frames->push_back(frame);
        return;
    }

    const char *origin = data.data();

    uint32_t current;
    for (current = 0; current < data_size - max_data_size_of_single_frame;
         current += max_data_size_of_single_frame) {
        uint32_t new_mask_key = mask_key;
        if (mask_key == 0) {
            new_mask_key = GenerateUINT32();
        }
        std::string slice(origin + current, max_data_size_of_single_frame);
        std::string frame =
            BuildSingleDataFrameImpl(false, mask, WS_CONTINUATION, new_mask_key, slice);
        frames->push_back(frame);
    }

    if (data_size > current) {
        uint32_t new_mask_key = mask_key;
        if (mask_key == 0) {
            new_mask_key = GenerateUINT32();
        }
        std::string slice(origin + current, data_size - current);
        std::string frame =
            BuildSingleDataFrameImpl(false, mask, WS_CONTINUATION, new_mask_key, slice);
        frames->push_back(frame);
    }

    std::string &f = frames->front();
    f[0] |= static_cast<char>(opcode); // set opcode
    std::string &b = frames->back();
    b[0] |= 0x80; // set fin
}

///////////////////////////////////////////////////////////////////////////////

namespace client {
std::string BuildHandshakeRequest(std::string *sec_websocket_key, const std::string &uri,
                                  const std::string &host, const std::string &origin,
                                  const std::string &sec_websocket_protocol) {
    char random_key[16] = {0};
    for (int i = 0; i < 4; i++) {
        uint32_t value = GenerateUINT32();
        memcpy(&random_key[i * 4], &value, 4);
    }

    Base64Encode(std::string(random_key, sizeof(random_key)), sec_websocket_key);

    char buff[1024] = {0};
    auto length     = snprintf(buff, sizeof(buff),
                               "GET %s HTTP/1.1\r\n"
                                   "Host: %s\r\n"
                                   "Upgrade: websocket\r\n"
                                   "Connection: Upgrade\r\n"
                                   "Sec-WebSocket-Key: %s\r\n"
                                   "Sec-WebSocket-Version: 13\r\n",
                               uri.data(), host.data(), sec_websocket_key->data());

    std::string request(buff, length);
    if (!sec_websocket_protocol.empty()) {
        request += WSH_SEC_PROTOCOL ": " + sec_websocket_protocol + "\r\n";
    }
    if (!origin.empty()) {
        request += WSH_ORIGIN ": " + origin + "\r\n";
    }
    request += "\r\n";
    return request;
}

std::string BuildCloseFrame(Status status, const std::string &debug, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    if (debug.size() > 0x7D) {
        return BuildCloseFrameImpl(true, mask_key, status, debug.substr(0, 0x7D));
    }
    return BuildCloseFrameImpl(true, mask_key, status, debug);
}
std::string BuildPingFrame(const std::string &data, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    if (data.size() > 0x7D) {
        return BuildPingPongFrameImpl(true, WS_PING, mask_key, data.substr(0, 0x7D));
    }
    return BuildPingPongFrameImpl(true, WS_PING, mask_key, data);
}
std::string BuildPongFrame(const std::string &data, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    if (data.size() > 0x7D) {
        return BuildPingPongFrameImpl(true, WS_PONG, mask_key, data.substr(0, 0x7D));
    }
    return BuildPingPongFrameImpl(true, WS_PONG, mask_key, data);
}

std::string BuildSingleDataFrame(const std::string &data, bool text_or_binary, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    int opcode = text_or_binary ? WS_TEXT : WS_BINARY;
    return BuildSingleDataFrameImpl(true, true, opcode, mask_key, data);
}

std::string BuildDataFrameHeader(const std::string &data, bool text_or_binary, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    int opcode = text_or_binary ? WS_TEXT : WS_BINARY;
    return BuildSingleDataFrameImpl(false, true, opcode, mask_key, data);
}
std::string BuildDataFrameBody(const std::string &data, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    return BuildSingleDataFrameImpl(false, true, WS_CONTINUATION, mask_key, data);
}
std::string BuildDataFrameTailer(const std::string &data, uint32_t mask_key) {
    if (mask_key == 0) mask_key = GenerateUINT32();
    return BuildSingleDataFrameImpl(true, true, WS_CONTINUATION, mask_key, data);
}
void BuildConsecutiveFrames(std::vector<std::string> *frames, const std::string &data,
                            uint32_t max_data_size_of_single_frame, bool text_or_binary,
                            uint32_t mask_key) {
    BuildConsecutiveFramesImpl(true, mask_key, frames, data, max_data_size_of_single_frame,
                               text_or_binary);
}
} // namespace client
namespace server {
std::string BuildHandshakeSuccessReply(const std::string &sec_websocket_key,
                                       const std::string &sec_websocket_protocol) {
    std::string sec_websocket_accept = BuildSecWebSocketAccept(sec_websocket_key);

    char buffer[2048]  = {0};
    int number_of_char = snprintf(buffer, sizeof(buffer),
                                  "HTTP/1.1 101 Switching Protocols\r\n"
                                  "Connection: Upgrade\r\n"
                                  "Upgrade: websocket\r\n"
                                  "Sec-WebSocket-Accept: %s\r\n",
                                  sec_websocket_accept.data());

    std::string response(buffer, number_of_char);
    if (!sec_websocket_protocol.empty()) {
        std::string sub_protocol;
        auto off = sec_websocket_protocol.find(',');
        if (off == std::string::npos) {
            sub_protocol = StringTrim(sec_websocket_protocol);
        } else {
            sub_protocol = StringTrim(sec_websocket_protocol.substr(0, off));
        }
        response += WSH_SEC_PROTOCOL ": " + sub_protocol + "\r\n";
    }
    response += "\r\n";
    return response;
}

std::string BuildHandshakeFailedReply(int status, const std::string &desc,
                                      const std::map<std::string, std::string> &fields) {
    char buff[1024]  = {0};
    auto num_of_char = snprintf(buff, sizeof(buff), "HTTP/1.1 %d %s\r\n", status, desc.data());
    std::string header(buff, num_of_char);

    for (auto it = fields.begin(); it != fields.end(); it++) {
        num_of_char =
            snprintf(buff, sizeof(buff), "%s: %s\r\n", it->first.data(), it->second.data());
        header += std::string(buff, num_of_char);
    }
    header += "\r\n";
    return header;
}

std::string BuildCloseFrame(Status status, const std::string &debug) {
    if (debug.size() > 0x7D) {
        return BuildCloseFrameImpl(false, 0, status, debug.substr(0, 0x7D));
    }
    return BuildCloseFrameImpl(false, 0, status, debug);
}
std::string BuildPingFrame(const std::string &data) {
    if (data.size() > 0x7D) {
        return BuildPingPongFrameImpl(false, WS_PING, 0, data.substr(0, 0x7D));
    }
    return BuildPingPongFrameImpl(false, WS_PING, 0, data);
}
std::string BuildPongFrame(const std::string &data) {
    if (data.size() > 0x7D) {
        return BuildPingPongFrameImpl(false, WS_PONG, 0, data.substr(0, 0x7D));
    }
    return BuildPingPongFrameImpl(false, WS_PONG, 0, data);
}
std::string BuildSingleDataFrame(const std::string &data, bool text_or_binary) {
    int opcode = text_or_binary ? WS_TEXT : WS_BINARY;
    return BuildSingleDataFrameImpl(true, false, opcode, 0, data);
}
std::string BuildDataFrameHeader(const std::string &data, bool text_or_binary) {
    int opcode = text_or_binary ? WS_TEXT : WS_BINARY;
    return BuildSingleDataFrameImpl(false, false, opcode, 0, data);
}
std::string BuildDataFrameBody(const std::string &data) {
    return BuildSingleDataFrameImpl(false, false, WS_CONTINUATION, 0, data);
}
std::string BuildDataFrameTailer(const std::string &data) {
    return BuildSingleDataFrameImpl(true, false, WS_CONTINUATION, 0, data);
}
void BuildConsecutiveFrames(std::vector<std::string> *frames, const std::string &data,
                            uint32_t max_data_size_of_single_frame, bool text_or_binary) {
    BuildConsecutiveFramesImpl(false, 0, frames, data, max_data_size_of_single_frame,
                               text_or_binary);
}
} // namespace server
} // namespace websocket
